34. Interface Implements & class Inheritance

(public) 상속은 실은 함수 인터페이스 상속과 함수 구현의 상속으로 나누어 진다.
class Shape{ //
public:
virtual void draw() const=0;
virtual void error(const std::string& msg);
int objectID() const;
// ...
};
class Rectangle: public Shape{ /* ... */ };
class Ellipse: public Shape{ /* ... */ };
Shape는 순수가상함수를 포함하는 추상 클래스이다.
(추상 클래스는 인스턴스를 만들 수 없으며, 파생 클래스만 인스턴스화 가능하다.)

draw는 순수 가상함수
error는 단순(비순수) 가상 함수
objectID는 비가상 함수로 선언되어 있다.
순수 가상 함수
순수 가상 함수는 기본 클래스를 상속한 파생함수에서 다시 선언해야 한다.
순수 가상 함수는 전형적으로 추상 클래스 안에서 정의를 가지고 있지 않다.
- 순수 가상 함수를 선언하는 목적은 파생 클래스에게 함수의 인터페이스만을 물려주는데 있다.

순수 가상 함수도 정의를 제공할 수는 있다.
(순수 가상 함수는 인스턴스화 할 수 없기 때문에 한정자를 붙여서 호출)
Shape* ps=new Shape; // error .
Shape* ps1=new Rectangle; // OK
ps1->draw(); // Rectangle::draw
Shape* ps2=new Ellipse; // OK
ps2->draw(); // Ellipse::draw
ps1->Shape::draw();
ps2->Shape::draw(); //
단순(비순수) 가상 함수
파생 함수로 하여금 함수의 인터페이스를 상속하게 한다는 점은 동일하지만,
파생 클래스 쪽에서 오버라이드 할 수 있는 함수 구현부도 제공한다.
- 단순 가상 함수를 선언하는 목적은 파생 클래스로 하여금 함수의 인터페이스 뿐만 아니라 함수의 기본 구현도
  물려주는데 있다.
class Shape{
public:
virtual void error(const::string& msg);
// ...
};
파생 클래스에서 error 함수를 정의할 것을 요구하지만, 정의하지 않더라도 기본 클래스의 error 함수를 사용한다.
class Airport{ /* ... */ };
class Airplane{
public:
virtual void fly(const Airport& destination); //
// ...
};
void Airplane::fly(const Airport& destination){
// act
}
class ModelA: public Airplane{ /* ... */ };
class ModelB: public Airplane{ /* ... */ };
fly를 단순 가상 함수로 선언하고 구현(정의)하였다.
단순 가상 함수를 선언과 정의를 하였기 때문에 ModelA와 ModelB에서 fly() 함수를 재정의하지 않을 경우,
Base class Airplane::fly()를 사용하게 된다.

함수를 재정의하는 것을 빼먹은 경우, 오류 없이 기본 클래스의 단순 가상 함수를 실행하기 때문에,
순수 가상 함수로 인터페이스를 제공하고, potected로 default 함수를 더 정의해 주는 것이 좋다.
(재정의를 까먹었을 경우, 에러를 출력할 것임)
class Airplane{
public:
virtual void fly(const Airport& destination)=0;
// ...
protected:
void defaultFly(const Airport& destination);
};
void Airplane::defaultFly(const Airport& destination){
// act
}
class ModelA: public Airplane{
public:
virtual void fly(const Airport& destination){ defaultFly(destination); }
// ...
};
class ModelB: public Airplane{
publiic:
virtual void fly(const Airport& destination){ defaultFly(destination); }
// ...
};
위와 같이 구현한 경우, 자신과 맞지 않는 기본 구현이 실수로 물려받아 실행되는 것을 막을 수 있다.

혹은 순수 가상 함수를 정의까지 해서 사용할 수 있다.
class Airplane{
public:
virtual void fly(const Airport& destination)=0; //
// ...
};
void Airplane::fly(const Airport& destination){ //
// act
}
class ModelA: public Airplane{
public:
virtual void fly(const Airport& destination){ Airplane::fly(destination); } //
};
class ModelB: public Airplane{
public:
virtual void fly(const Airport& destination){ Airplane::fly(destination); } //
};
class ModelC: public Airplane{
public:
virtual void fly(const Airport& destination);
// ...
};
void ModelC::fly(const Airport& destination){
// act for ModelC
}
비가상 함수
class Shape{
public:
int objectID() const;
// ...
};
비가상 함수는 파생 클래스에서 다른 행동이 일어날 것으로 가정하지 않음을 의미한다.
비가상 멤버 함수는 클래스 파생에 상관없이 변하지 않는 동작 함수를 정의할 때 사용한다.
- 비가상 함수를 선언하는 목적은 파생 클래스가 함수 인터페이스와 더불어
  그 함수의 필수적인 구현(mandatory implementation)을 물려 받게 한다.

비가상 함수에 대하여 파생 클래스에서 재정의(오버라이딩)하지 말 것
비가상 소멸자는 상속시에 오버라이딩 됐을 때, 문제를 야기 할 수 있다.